package com.dayuanit.dy15.atm.service.impl;

import com.dayuanit.dy15.atm.dto.TransferOrderDTO;
import com.dayuanit.dy15.atm.entity.Card;
import com.dayuanit.dy15.atm.entity.TransferOrder;
import com.dayuanit.dy15.atm.enums.FlowTypeEnum;
import com.dayuanit.dy15.atm.enums.TransferStatusEnum;
import com.dayuanit.dy15.atm.exception.BizException;
import com.dayuanit.dy15.atm.holder.PageHolder;
import com.dayuanit.dy15.atm.mapper.TransferOrderMapper;
import com.dayuanit.dy15.atm.util.ApiConnector;
import com.dayuanit.dy15.atm.util.DateUtil;
import com.dayuanit.dy15.atm.util.MoneyUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service("cardServiceTransferImpl")
public class CardServiceTransferImpl extends CardServiceImpl {
    private static final Logger logger = LoggerFactory.getLogger(CardServiceImpl.class);

    @Autowired
    private TransferOrderMapper transferOrderMapper;

    @Value("${atm.transfer.delay}")
    private int transferDelaySecond;


    /**
     * 异步转账
     * @param cardId
     * @param password
     * @param amount
     * @param cardNumIn
     * @param userId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void transfer(long cardId, String password, String amount, String cardNumIn, long userId) {
        System.out.println(">>>>>>>>>异步转账");
        // 第一步 钱出去 生成流水
        if (StringUtils.isBlank(password) ||
                StringUtils.isBlank(amount) ||
                StringUtils.isBlank(cardNumIn)) {
            throw new BizException("有必填参数为空");
        }

        Card cardOut = cardMapper.getById(cardId);
        Card cardIn = cardMapper.getByCardNum(cardNumIn);
        if (null == cardOut || null == cardIn) {
            throw new BizException("银行卡不存在或者密码错误");
        }

        if (!cardOut.getPassword().equals(password)) {
            throw new BizException("卡号不存在或者密码错误");
        }

        //悲观锁 锁住转出账户
        cardOut = cardMapper.getById4Lock(cardId);
        if (userId != cardOut.getUserId()) {
            throw new BizException("无权操作");
        }

        //校验金额  转账金额的范围 限额的范围
        if (Double.parseDouble(amount) <= 0){
            throw new BizException("转账金额必须大于零");
        }

        //减钱，添加流水
        subBalance(cardOut, amount, FlowTypeEnum.TRANSFER_OUT);

        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.SECOND, transferDelaySecond);

        // 第二步 添加转账订单
        TransferOrder transferOrder = new TransferOrder();
        transferOrder.setAmount(MoneyUtil.yuanTofen(amount));
        transferOrder.setCardInId(cardIn.getId());
        transferOrder.setCardOutId(cardOut.getId());
        transferOrder.setCreateTime(new Date());
        transferOrder.setModifyTime(transferOrder.getCreateTime());
        transferOrder.setStatus(TransferStatusEnum.待转账.getK());
        transferOrder.setUserId(userId);
        transferOrder.setDeathTime(cal.getTime());
        transferOrderMapper.insert(transferOrder);
    }


    @Override
    public List<TransferOrder> listTransferOrder4In() {
        List<TransferOrder> transferOrders = transferOrderMapper.listOrder(TransferStatusEnum.待转账.getK());
        return transferOrders;
    }


    /**
     * 不推荐!!!!
     * 由于在同一事务中，处理多笔转账订单，那么，就会出现，有一笔失败，导致这次所有订单都会回滚，
     * 显然这样的设计是有问题的，不靠谱的。
     * 应该是，一笔一笔的处理。也就是在一个事务中，处理一笔订单。
     *
     *  **********  事务失效三种方式：**************
     * 1.抛出的异常是检查型的，spring默认不处理。
     * 2.异常木有抛出，spring不会回滚。
     * 3.在同一个service中，调用同一个类的方法，当一个方法没开启事务，之后的方法调用也不会有事务，即使
     *   下面的方法有事务注释，也没用。
     * @param orders
     */
    @Override
    public void processTransferIn(List<TransferOrder> orders) {

        System.out.println("*******************" + this.getClass().getName());
        for (TransferOrder order : orders) {
//            try {
                processTransferIn(order);
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Card processTransferIn(TransferOrder order) {
        Card cardIn = cardMapper.getById4Lock(order.getCardInId());

        //加钱 加流水
        plusBlance(cardIn, MoneyUtil.fenToYuan(order.getAmount()), FlowTypeEnum.TRANSFER_IN);

        //修改转账订单的状态为成功
        int rows = transferOrderMapper.updateStatus(order.getId(), order.getStatus(), TransferStatusEnum.转账成功.getK());
        if (1 != rows) {
            throw new BizException("异步转账失败");
        }

        return cardIn;
    }

    /**
     * 转账失败 将钱还回去到转出账户
     * @param order
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void processTransferBack(TransferOrder order) {
        Card cardOut = cardMapper.getById4Lock(order.getCardOutId());
        plusBlance(cardOut, MoneyUtil.fenToYuan(order.getAmount()), FlowTypeEnum.TRANSFER_BACK);

        //修改转账订单的状态为成功
        int rows = transferOrderMapper.updateStatus(order.getId(), order.getStatus(), TransferStatusEnum.已取消.getK());
        if (1 != rows) {
            throw new BizException("转账退款失败");
        }
    }


    @Override
    public void changeTransferOrder4Fail(TransferOrder order) {
        transferOrderMapper.updateStatus(order.getId(), order.getStatus(), TransferStatusEnum.转账失败.getK());
    }

    @Override
    public List<TransferOrder> listTransferOrder4Back() {
        List<TransferOrder> transferOrders = transferOrderMapper.listOrder4Back(TransferStatusEnum.转账失败.getK());
        return transferOrders;
    }

    @Override
    public PageHolder<List<TransferOrderDTO>> listOrders(long userId, int currentPage, int pageSize) {

        int total = transferOrderMapper.countByUserId(userId);
        PageHolder<List<TransferOrderDTO>> pageHolder = PageHolder.init(currentPage, total, pageSize);

        //每页显示的数据
        List<TransferOrder> transferOrders = transferOrderMapper.listByUserId(userId, pageHolder.offset(), pageSize);

        List<TransferOrderDTO> dtos = new ArrayList<>(transferOrders.size());
        for (TransferOrder order : transferOrders) {
            TransferOrderDTO transferOrderDTO = new TransferOrderDTO();
            dtos.add(transferOrderDTO);

                //不推荐 每次循环 都要多查两次  成本太高
//            Card cardIn = cardMapper.selectByPrimaryKey(order.getCardInId());
//            Card cardOut = cardMapper.selectByPrimaryKey(order.getCardOutId());

            transferOrderDTO.setAmount(MoneyUtil.fenToYuan(order.getAmount()));
//            transferOrderDTO.setCardInNum(cardIn.getCardNum());
//            transferOrderDTO.setCardOutNum(cardOut.getCardNum());
            transferOrderDTO.setCardInId(order.getCardInId());
            transferOrderDTO.setCardOutId(order.getCardOutId());
            transferOrderDTO.setCreateTime(DateUtil.date2String(order.getCreateTime()));
            transferOrderDTO.setOrderId(order.getId());
            transferOrderDTO.setOrderStatus(TransferStatusEnum.get(order.getStatus()).getV());
            transferOrderDTO.setStatus(order.getStatus());
        }

        //把转入和转出的卡号ID取出来 （有问题，List集合里面可能会有重复的cardId）
        //HashSet(无序，元素不重复)

        //不推荐List集合，因为元素会重复
//        List<Long> cardIds = new ArrayList<>(transferOrders.size() * 2);

        //推荐使用HashSet集合，自动去重，因为底层用的是HashMap，HashMap的key不重复。
        Set<Long> cardIds = new HashSet<>();
        for (TransferOrder order : transferOrders) {
//            cardIds.add(order.getCardInId());
//            cardIds.add(order.getCardOutId());
            cardIds.add(order.getCardOutId());
            cardIds.add(order.getCardInId());
        }

        //将DTO的转入和转出卡号赋值
        List<Card> cards = cardMapper.listById(cardIds);
        for (TransferOrderDTO dto : dtos) {
            for (Card card : cards) {
                if (dto.getCardInId() == card.getId()) {
                    dto.setCardInNum(card.getCardNum());
                }

                if (dto.getCardOutId() == card.getId()) {
                    dto.setCardOutNum(card.getCardNum());
                }
            }
        }

        pageHolder.setData(dtos);

        return pageHolder;
    }

    /**
     * 取消转账订单
     * @param userId
     * @param orderId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelTransfer(long userId, long orderId) {
        TransferOrder order = transferOrderMapper.selectByPrimaryKey(orderId);
        if (null == order || order.getUserId() != userId) {
            throw new BizException("无权操作");
        }

        //只有待转账的订单才有资格取消
        if (order.getStatus() != TransferStatusEnum.待转账.getK()) {
            throw new BizException("无法取消订单");
        }

        int rows = transferOrderMapper.updateStatus(orderId, order.getStatus(), TransferStatusEnum.已取消.getK());
        if (1 != rows) {
            throw new BizException("订单取消失败");
        }

        Card cardOut = cardMapper.getById4Lock(order.getCardOutId());
        plusBlance(cardOut, MoneyUtil.fenToYuan(order.getAmount()), FlowTypeEnum.TRANSFER_BACK);
    }
}
